package gweb

import (
	"fmt"
	"net/http"
	"strings"

	"gitee.com/makitdone/gweb/v2/conf"
	"gitee.com/makitdone/gweb/v2/errors"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

// 必须是超级管理员
func MustBeSuperuser() HandlerFunc {
	return func(c *Context) {
		claim := c.GetClaim()
		if claim != nil && claim.IsSuperuser {
			c.Next()
		} else {
			c.AbortWithStatusJSON(403, gin.H{"message": "Forbidden, should be superuser"})
		}
	}
}

// 系统维护时放行
func AllowIfMaintain() HandlerFunc {
	return func(c *Context) {
		route := c.Route
		route.SetInfo("maintain_allow", true)
		c.Next()
	}
}

func middlewareRecover() HandlerFunc {
	return func(c *Context) {
		app := c.App
		logger := c.App.logger

		defer func() {
			if e := recover(); e != nil {
				if res, ok := e.(errors.ErrorPrompt); ok {
					// 如果是系统提示，直接响应
					c.Fail(res.Message)
				} else {
					if gorm.ErrRecordNotFound == e {
						c.JSON(http.StatusNotFound, gin.H{
							"succeeded": false,
							"message":   "数据未找到",
						})
					} else {
						// 如果是错误，则记录路由信息，并打印异常堆栈
						userId := c.GetUint("userID")
						logger.Errorf("user %d, %s %s", userId, c.Request.Method, c.Request.URL)
						err := errors.New(fmt.Sprintf("%v", e))
						traceback := fmt.Sprintf("Traceback:\n%+v", err)
						logger.Errorf("%s\n", traceback)

						config := app.GetConfig()
						if config.Env == conf.EnvProd {
							c.JSON(http.StatusInternalServerError, gin.H{
								"succeeded": false,
								"message":   "服务出错",
							})
						} else {
							c.String(http.StatusInternalServerError, traceback)
						}
						appHandlers := app.handlers
						if len(appHandlers) > 0 {
							for _, handler := range appHandlers {
								handler.OnError(app, err, traceback)
							}
						}
					}
				}
				c.Abort()
			}
		}()
		c.Next()
	}
}

// CORS 跨域中间件
func middlewareCors(app *GinApp) gin.HandlerFunc {
	return func(c *gin.Context) {
		config := app.config.Cors
		if config == nil || !config.Enable {
			c.Next()
			return
		}

		method := c.Request.Method
		origin := c.Request.Header.Get("Origin")
		allowHeader := "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-Receiver-Id"
		allowMethods := "POST,GET,OPTIONS,DELETE,PUT,HEAD,PATCH"
		exposeHeaders := "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type, New-Token, New-Expires-At"
		allowCredentials := true

		// 检查是否在黑名单
		for _, item := range config.Blacklist {
			if origin == item {
				c.AbortWithStatus(http.StatusForbidden)
				return
			}
		}

		allowed := len(config.Whitelist) == 0
		// 遍历配置中的跨域头，寻找匹配项
		for _, item := range config.Whitelist {
			if origin == item.AllowOrigin {
				allowHeader = item.AllowHeaders
				allowMethods = item.AllowMethods
				exposeHeaders = item.ExposeHeaders
				allowCredentials = item.AllowCredentials
				allowed = true
				break
			}
		}

		if allowed {
			c.Header("Access-Control-Allow-Origin", origin)
			c.Header("Access-Control-Allow-Headers", allowHeader)
			c.Header("Access-Control-Allow-Methods", allowMethods)
			c.Header("Access-Control-Expose-Headers", exposeHeaders)
			if allowCredentials {
				c.Header("Access-Control-Allow-Credentials", "true")
			}

			// 放行所有OPTIONS方法
			if method == "OPTIONS" {
				c.AbortWithStatus(http.StatusNoContent)
			} else {
				// 处理请求
				c.Next()
			}

		} else {
			c.AbortWithStatus(http.StatusForbidden)
		}
	}
}

func MiddlewareJWT(config *conf.AppConfig) HandlerFunc {
	return func(c *Context) {
		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			authHeader = c.GetHeader("Token")
		}
		if authHeader == "" {
			c.JSON(http.StatusForbidden, map[string]any{
				"message": "请求头中缺少 Authorization",
			})
			c.Abort()
			return
		}
		parts := strings.SplitN(authHeader, " ", 2)
		if !(len(parts) == 2 && parts[0] == "JWT") {
			c.JSON(http.StatusForbidden, map[string]any{
				"message": "请求头中token格式错误",
			})
			c.Abort()
			return
		}
		jwt := &JWT{JwtConfig: config.JWT}
		if claim, err := jwt.ParseToken(parts[1]); err != nil {
			c.JSON(http.StatusForbidden, map[string]any{
				"message": "无效token",
			})
			c.Abort()
			return
		} else {
			c.Set("claim", claim)
		}

		c.Next()
	}
}
